(function(){
var AC = window.AliCert = {};
var initialized = false;

/**
 * 常用的工具函数
 */
var util = AC.util = {
	log: function(msg) {
		if (!AC.debug) return;
		if (window.console && console.log) {
			console.log(msg);
		}else {
			alert(msg);
		}
	},
	extend: function() {
		var args = arguments;
		if (!args[1]) {
			args = [this, args[0]];
		}
		for (var property in args[1]) {
			args[0][property] = args[1][property];
		}
		return args[0];
	},
	treatUrl: function(url) {
		var trueUrl,
			isSingleServer = /download|img/.test(url);
		if (location.protocol == 'http:') {
			if (isSingleServer) {
				trueUrl = 'http:' + url.slice(url.indexOf('//')).replace(/.alipay.com/, '.alipay.net');
			}else {
				trueUrl = 'http:' + url.slice(url.indexOf('//')).replace(/.alipay.com/, location.hostname.slice(location.hostname.indexOf('.')));
			}
		}else if (location.protocol == 'https:') {
			trueUrl = url;
		}
		return (trueUrl);
	},
	detectPlugin: function(pluginName, mimeType) {
		return navigator.plugins && navigator.plugins[pluginName] && navigator.mimeTypes && navigator.mimeTypes[mimeType] && navigator.mimeTypes[mimeType].enabledPlugin ? navigator.plugins[pluginName] : false;
	},
	detectActiveX: function(objectName) {
		var obj = false;
		try {
			obj = new ActiveXObject(objectName);
		}catch (e) {}
		return obj;
	},
	createObject: function(id, codebase, classid) {
		var objstr = '<object id="' + id + '" codebase="' + codebase + '" classid="' + classid + '" width="0" height="0" onreadystatechange="javascript:window.onReadyActiveX(id);"></object>';
		document.write(objstr);
	},
	createEmbed: function(id, type, data) {
		var objstr = '<object id="' + id + '" type="' + type + '" data="' + data + '" width="0" height="0"></object>';
		document.write(objstr);
		return document.getElementById(id);
	}
};

var track = function() {
	var buffer = [],
		send = function(seed) {
			if(window.Tracker){
				Tracker.click(seed);
			} else {
				buffer.push(seed);
				window.setTimeout(function() {
					send(buffer.shift());
				}, 100);
			}
		};
	return function(seed, withClientInfo) {
		if(!seed) return;
		if(withClientInfo && window.light) {
			var ua = light.client.info,
				ver = ua.browser.version;
			ver = ver ? ver[0] : 'na';
			seed += '-' + (ua.browser.name || 'na') + '/' + ver;
		}
		send(seed);
	};
}();


/**
 * 证书下载地址的配置
 */
var certClsid = {
		enroll: 'clsid:7978461C-CC22-48F2-BC69-02220D3E101D',
		pta: 'clsid:1E0DFFCF-27FF-4574-849B-55007349FEDA'
	},
	certCab = {
		enroll: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ie/iTrusPTA.cab'),
		pta: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ie/PTA.cab')
	},
	certExe = {
		'win': {
			ie: {
				enroll: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ie/iTrusPTA.exe'),
				pta: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ie/iTrusPTA.exe')
			},
			nonie: {
				enroll: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ff/iTrusPTA_f_c.exe?t=20110714'),
				pta: util.treatUrl('https://download.alipay.com/ukey/cert/1007/ff/iTrusPTA_f_c.exe?t=20110714')
			}
		}
	};
/**
 * 证书调用异常时的错误码和错误信息
 */
var ieErrorCode = {
		'-2147483135' : 'userCancel',
		 '-2147467238' : 'passwordError',
		 '-2146885630' : 'pfxFileError',
		 '-2146823281' : 'certNotFound',
		 '-2146893813' : 'notExport'
	},
	nonieErrorCode = {
		'0x80000201' : 'userCancel',
		 '0x8000401a' : 'passwordError',
		 '0x80000202' : 'pfxFileError',
		 '0x800a138f' : 'certNotFound',
		 '0x8009000a' : 'notExport'
	},
	errorMsg = {
		'installError': '对不起，数字证书的安装有误',
		'needInstall-IE': '您还没有安装数字证书的控件！',
		'needInstall-notIE': '您还没有安装数字证书的控件！',
		'userCancelCert': '您已经取消数字证书申请！',
		'isValidBrowser': '对不起，数字证书暂时不支持您的浏览器。',
		'noImplement': '控件不支持此种调用',
		'csrError': '创建证书请求出错',
		'userCancel': '用户选择取消',
		'passwordError': '密码错误',
		'pfxFileError': '证书文件错误',
		'dummyError': '未知错误',
		'certNotFound': '找不到证书',
		'notExport': '备份时选择不允许导出证书',
		'removeError': '证书删除错误，请重新操作'
	};
/**
 * 证书调用中使用的常量
 */
AC.CONSTANTS = {
	KEY_USAGE_UNDEFINED: 0x00,
	KEY_USAGE_CRL_SIGN: 0x02,
	KEY_USAGE_CERT_SIGN: 0x04,
	KEY_USAGE_KEY_AGREEMENT: 0x08,
	KEY_USAGE_DATA_ENCIPHERMENT: 0x10,
	KEY_USAGE_KEY_ENCIPHERMENT: 0x20,
	KEY_USAGE_NON_REPUDIATION: 0x40,
	KEY_USAGE_DIGITAL_SIGNATURE: 0x80,

	KEY_LENGTH_1024: 0x04000000,
	KEY_LENGTH_512: 0x02000000,

	CRYPT_USER_PROTECTED: 0x2,
	CRYPT_EXPORTABLE: 0x1,

	INPUT_BASE64: 0x01,
	INPUT_HEX: 0x02,
	OUTPUT_BASE64: 0x04,
	OUTPUT_HEX: 0x08,
	MSG_BASE64: 0x04,
	MSG_HEX: 0x08
};
/**
 * 配置项
 */
var cfg = AC.config = {debug: false, timer: null, timerCounter: 0};
var opt = AC.options = {
	exceptionShowUrl: 'https://securitycenter.alipay.com/sc/checkItrusenroll.htm',
	needInstall: 'all', //需要安装控件类型all, pta, enroll
	installedFun: null, //安装成功回调函数
	notInstalledFun: null, //安装失败回调函数
	installCallFun: null, //安装过程回调函数
	isValidBrowserFun: null //浏览器不支持回调函数

};

window.onReadyActiveX = function(id) {
	var obj = document.getElementById(id);
	if (obj && obj.readyState == 4) {
		if (id == 'iTrusPTA') {AC.iTrusPTA = obj.object;}
		if (id == 'iTrusEnroll') {AC.iTrusEnroll = obj.object;}
	}
};

//确定客户端信息
AC.env = (function() {
	var ua = navigator.userAgent.toLowerCase();
	return {
		os: {
			win: /window|win32/.test(ua),
			linux: /linux/.test(ua)
		},
		browser: {
			ie: /msie/.test(ua),
			chrome: /webkit/.test(ua) && /chrome|chromium/.test(ua),
			newChrome: false,
			firefox: /gecko/.test(ua) && !/webkit/.test(ua) && /firefox/.test(ua)
		}
	};
})();

// 比较 v1 版本号是否大于等于 v2
AC.hasVersion = function(ver1, ver2) {
	if(!ver1) return false;
	if(!ver2) return true;

	var v1, v2;
	for(var i = 0, len = Math.max(ver1.length, ver2.length); i < len; i++) {
		v1 = parseInt(ver1[i], 10) || 0; v2 = parseInt(ver2[i], 10) || 0;
		if(v1 !== v2) return v1 > v2;
	}
	// version equals completely
	return true;
};

if(AC.env.browser.chrome) {
	var ver = /(?:Chrome|Chromium)\/(\d+)\.(\d+)\.(\d+)/.exec(navigator.userAgent);
	if(ver && ver.length === 4) {
		ver.shift();
		AC.env.browser.newChrome = AC.hasVersion(ver, [12, 0, 742]);
	}
}

//确定下载地址
AC.itrusUrl = (function() {
	var _url = null;
	if (AC.env.browser.ie) {
		_url = certExe.win.ie.enroll;
	} else if ( AC.env.os.win && (AC.env.browser.chrome || AC.env.browser.firefox) ) {
		_url = certExe.win.nonie.enroll;
	}
	return _url;
})();


//根据是否有下载判断是否支持当前浏览器
AC.isValidBrowser = AC.itrusUrl ? true : false;

/**
 * exception异常处理对象
 */
var exp = AC.exception = {
	codes: AC.env.browser.ie ? ieErrorCode : nonieErrorCode,
	code: '',
	msgs: errorMsg,
	msg: '',
	xBox: null,
	showTip: function(id, status) {
		if (typeof id == 'string') {
			document.getElementById(id).innerHTML = this.msgs[status];
		}
	},
	xBoxInit: function() {
		try {
			var checkItrusenrollUrl = util.treatUrl(opt.exceptionShowUrl);
			exp.xBox = new AP.widget.xBox({
			   el: false,
			   type: 'iframe',
			   load: true,
			   modal: false,
			   width: 600,
			   height: 500,
			   value: function(a) {
				  return checkItrusenrollUrl + '?status=' + a.s + '&code=' + a.c;
			   },
			   onShowEvent: function() {
					var t = setInterval(function() {
						 if (D.get('xbox-iframe') && D.get('xbox-iframe').contentWindow.document) {
							 var xboxIfr = D.get('xbox-iframe').contentWindow.document;
							 var certDownLink = xboxIfr.getElementById('certDownLink');
							 if (certDownLink) {
								 clearInterval(t);
								 certDownLink.href = AC.itrusUrl;
							 }
						 }else {
							 clearInterval(t);
						 }
					 }, 1000);
			   }
			});
		}catch (e) {}
	},
	showBox: function(status) {
		var cs = status ? status : this.msg;
		if (this.xBox) {
			this.xBox.fire({'s': cs, 'c': this.code});
		}else {
			alert(this.msgs[cs]);
			if (cs == 'needInstall-notIE' || cs == 'needInstall-IE') {
				location.href = AC.itrusUrl;
			}
		}
	}
};

/**
 * X509Cert证书对象
 */
var X509Cert = function(cert) {
	try{
		this.cert = cert;
		this.CommonName = cert.CommonName;
		this.Subject = cert.Subject;
		this.Issuer = cert.Issuer;
		this.KeyContainer = cert.KeyContainer;
		this.SerialNumber = cert.SerialNumber;
		this.ValidForm = eval(cert.ValidForm);
		this.ValidTo = eval(cert.ValidTo);
	}catch(ex){}
};
X509Cert.prototype = {
	remove: function() {
		try {
			this.cert.Remove();
			return true;
		}catch (ex) {
			var e = exp.code = ex.number || AC.iTrusPTA.ErrorCode;
			exp.msg = exp.codes[e] ? exp.codes[e] : 'removeError';
			return false;
		}
    },
	signLogonData: function(reqtext, signOpt) {
		var rt = '';
		try {
			rt = this.cert.SignLogonData(reqtext, signOpt);
		}catch (ex) {
			var e = exp.code = typeof AC.iTrusPTA.ErrorCode !== 'undefined' ? AC.iTrusPTA.ErrorCode : ex.number;
			exp.msg = exp.codes[e] || 'unknownError';
			// if(e < 0) e = '0x' + (Math.abs(e) * 2).toString(16);
			track('itruscert-sign-' + e, true);
		}
		return rt;
	},
	//return filepath
	exportPKCS12: function(bakpass, OptionExport, filename) {
		var filepath = '';
		try {
			filepath = this.cert.ExportPKCS12(bakpass, OptionExport, filename);
		}catch (ex) {
			var e = exp.code = ex.number || AC.iTrusPTA.ErrorCode;
			exp.msg = exp.codes[e] ? exp.codes[e] : 'unknownError';
		}
		return filepath;
	}
};

//win-xcert
var winXCert = {
	//iTrusEnroll
	iTrusEnroll: null,
	//iTrusPTA
	iTrusPTA: null,
	
	ready: false,
	_readyList: [],

	onReady: function(callback){
		if(this.ready){
			callback.apply(this);
		}else{
			this._readyList.push(callback);
		}
	},
	api: function(obj, prop, value) {
		if(!obj || !prop) return;

		try {
			if(arguments.length == 2) {
				return obj[prop];
			} else {
				obj[prop] = value;
			}
		} catch(e) {
			var num = typeof obj.ErrorCode === 'undefined' ? e.number : obj.ErrorCode;
			// if(num < 0) num = '0x' + (Math.abs(num) * 2).toString(16);
			track('itruscert-' + prop + '-' + num, true);
		}
		return null;
	},
	//init iTrusEnroll & iTrusPTA
	render: function() {
		var pta, enroll, mimeType, NPComBrg,
			need = opt.needInstall,
			iTrusPTA = document.getElementById('iTrusPTA'),
			iTrusEnroll = document.getElementById('iTrusEnroll');
			
		opt.installCallFun && opt.installCallFun('checking');
		if (window.ActiveXObject) {
			pta = util.detectActiveX('PTA.iTrusPTA');
			enroll = util.detectActiveX('Itrusenroll.CertEnroll');
			if ((need == 'all' || need == 'pta') && !iTrusPTA) {
				util.createObject('iTrusPTA', certCab.pta, certClsid.pta);
			}
			if ((need == 'all' || need == 'enroll') && !iTrusEnroll) {
				util.createObject('iTrusEnroll', certCab.enroll, certClsid.enroll);
			}
			cfg.timerCounter = 0;
			cfg.timer = setInterval(function(fn) {
				var	ac = AliCert, need = ac.options.needInstall,
					installedPTA = (need == 'pta') && ac.iTrusPTA,
					installedEnroll = (need == 'enroll') && ac.iTrusEnroll,
					installedAll = (need == 'all') && ac.iTrusEnroll && ac.iTrusPTA,
					result = installedPTA || installedEnroll || installedAll;
				onReadyCallFun(result, 'needInstall-IE');
			}, 100);
		}else {
			NPComBrg = false;
			//Firefox浏览器先检测是否安装老控件，已安装不用更新，未安装要求安装新插件
			if(AC.env.browser.firefox){
				mimeType = 'application/PTA.iTrusPTA';
				NPComBrg = util.detectPlugin('iTrusChina iTrusPTA,XEnroll,iEnroll,hwPTA,UKeyInstalls Firefox Plugin', mimeType);
				if(NPComBrg){ //已安装，则初始化iTrusPTA和iTrusEnroll对象
					if ((need == 'all') || (need == 'pta')) {
						this.iTrusPTA = iTrusPTA || util.createEmbed('iTrusPTA', 'application/PTA.iTrusPTA', certExe.win.nonie.pta);
					}
					if ((need == 'all') || (need == 'enroll')) {
						this.iTrusEnroll = iTrusEnroll || util.createEmbed('iTrusEnroll', 'application/Itrusenroll.CertEnroll', certExe.win.nonie.enroll);
					}
				}
			}
			if(!NPComBrg){ //非Firefox浏览器，或者Firefox未安装旧版本插件，则要求安装新插件
				mimeType = this.env.browser.firefox ? 'application/PTA.iTrusPTA.Version.1' : 'application/pta.itruspta.version.1';
				NPComBrg = util.detectPlugin('iTrusChina iTrusPTA,XEnroll,iEnroll,hwPTA,UKeyInstalls Firefox Plugin', mimeType);
				if ((need == 'all') || (need == 'pta')) {
					this.iTrusPTA = iTrusPTA || util.createEmbed('iTrusPTA', 'application/PTA.iTrusPTA.Version.1', certExe.win.nonie.pta);
				}
				if ((need == 'all') || (need == 'enroll')) {
					this.iTrusEnroll = iTrusEnroll || util.createEmbed('iTrusEnroll', 'application/Itrusenroll.CertEnroll.Version.1', certExe.win.nonie.enroll);
				}
			}
			//firefox控件调用的是ie下的控件，所以还需要在firefox判断ie下的pta,ienroll控件是否安装
			cfg.timerCounter = 0;
			cfg.timer = setInterval(function(fn) {
				var	ac = AliCert, need = ac.options.needInstall,
					ptaVersion = ac.getPTAVersion(),
					enrollVersion = ac.getEnrollVersion(),
					installedPTA = (need == 'pta') && ptaVersion && NPComBrg,
					installedEnroll = (need == 'enroll') && enrollVersion && NPComBrg,
					installedAll = (need == 'all') && ptaVersion && enrollVersion && NPComBrg;
					result = installedPTA || installedEnroll || installedAll;
				// 新版Chrome上的旧控件，单独给出Chrome插件升级的浮层
				onReadyCallFun(result, (NPComBrg && ac.env.browser.newChrome) ? 'needUpgrade-Chrome' : 'needInstall-notIE');
			}, 100);
		}
		window.onReadyCallFun = function(result, needInstall) {
			cfg.timerCounter++;
			if (result) {
				clearInterval(cfg.timer);
				opt.installedFun && opt.installedFun();
				opt.installCallFun && opt.installCallFun('checkedOK');
				AC.ready = true;
				while(AC._readyList.length) AC._readyList.shift().apply(AC);
			}ehttp://login.taobao.com/lse if (cfg.timerCounter > 10) {
				clearInterval(cfg.timer);
				exp.msg = needInstall;
				opt.notInstalledFun && opt.notInstalledFun();
				opt.installCallFun && opt.installCallFun('checkedError');
			}
		};
	},
	createPKCS10: function(certApplyTitle, certApplyCSPName, certApplyHashAlg, exportable, protectable, certApplyKeyLength, certUseage) {
		var keyflags = 0;
		if (exportable) {
			keyflags = keyflags | this.CONSTANTS.CRYPT_EXPORTABLE;
		}
		if (protectable) {
			keyflags = keyflags | this.CONSTANTS.CRYPT_USER_PROTECTED;
		}
		this.iTrusEnroll.Reset();
		this.iTrusEnroll.ProviderType = 1;
		this.iTrusEnroll.ProviderName = certApplyCSPName;
		this.iTrusEnroll.HashAlgorithm = certApplyHashAlg;
		this.iTrusEnroll.KeySpec = 1;
		if (512 == certApplyKeyLength) {
			certApplyKeyLength = this.CONSTANTS.KEY_LENGTH_512;
		}else {
			certApplyKeyLength = this.CONSTANTS.KEY_LENGTH_1024;
		}
		this.iTrusEnroll.GenKeyFlags = certApplyKeyLength | keyflags;
		var csr = null;
		try {
			csr = this.iTrusEnroll.createPKCS10(certApplyTitle, certUseage);
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
			exp.msg = 'csrError';
			window.Tracker && Tracker.click('certEx'+exp.code);
		}
		return csr;
	},
	acceptPKCS7: function(certChain) {
		try {
			this.iTrusEnroll.Reset();
			this.iTrusEnroll.DeleteRequestCert = false;
			this.iTrusEnroll.WriteCertToCSP = true;
			this.iTrusEnroll.acceptPKCS7(certChain);
			return true;
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
			exp.showBox('installError');
			window.Tracker && Tracker.click('certEx'+exp.code);
			return false;
		}
	},
	setFilters: function(issuer, subject,sn, csp, useLocalMachineCertStore ) {
		var certFilter = this.iTrusPTA.Filter;
		certFilter.Clear();
		issuer && this.api(certFilter, 'Issuer', issuer);
		if (sn) {
			if(parseInt(sn.substr(0, 1), 16) > 8) { sn = '00' + sn;}
			this.api(certFilter, 'SerialNumber', sn);
		}
		subject && this.api(certFilter, 'Subject', subject);
	},
	getCerts: function(issuer, subject, sn) {
		var certs_array = [], certs;

		this.setFilters(issuer, subject, sn);
		certs = this.api(this.iTrusPTA, 'MyCertificates');
		for (var i = 1; i <= certs.Count; i++) {
			certs_array.push(new X509Cert(certs(i)));
		}
		return certs_array;
	},
	getSingleCert: function(issuer, subject, sn) {
		var certs = this.getCerts(issuer, subject, sn);
		if (certs.length > 0) {
			return (certs[0]);
		}else {
			return null;
		}
	},
	//return cert
	importPKCS12: function(certpath, password) {
		var rt = null;
		try {
			rt = new X509Cert(this.iTrusPTA.ImportPKCS12(certpath, password, 0));
		}catch (ex) {
			var e = exp.code = ex.number || this.iTrusPTA.ErrorCode;
			exp.msg = exp.codes[e] ? exp.codes[e] : 'unknownError';
		}
		return rt;
	},
	//证书导出return filepath
	getExportPath: function(filename) {
		var rt = '';
		try {
			rt = this.iTrusEnroll.GetExportPath(filename);
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
		}
		return rt;
	},
	//证书导入return filepath
	getImportPath: function(filename) {
		var rt = '';
		try {
			rt = this.iTrusEnroll.GetImportPath(filename);
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
		}
		return rt;
	},
	getPTAVersion: function() {
		// Chrome 12+ 要求使用新控件
		if(AC.env.browser.newChrome) {
			var plugin = navigator.plugins['iTrusChina iTrusPTA,XEnroll,iEnroll,hwPTA,UKeyInstalls Firefox Plugin'];
			if(!plugin) return 0;
			// 新版非IE控件版本信息放在description中
			var ver = /version=(\d)\.(\d)\.(\d).(\d)/.exec(plugin.description);
			if(!ver) return 0;
			ver.shift();
			if(!AC.hasVersion(ver, [1, 0, 0, 2])) return 0;
		}

		try {
			return this.iTrusPTA.Version;
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
			exp.msg = 'noImplement';
			return 0;
		}
	},
	getEnrollVersion: function() {
		try {
			return this.iTrusEnroll.Version();
		}catch (ex) {
			exp.code = ex.number || this.iTrusPTA.ErrorCode;
			exp.msg = 'noImplement';
			return null;
		}
	}
};

//##linux-xcert
var linuxXCert = {};

if (AC.env.os.linux) {
	util.extend(AC, linuxXCert);
}else{
	// 有些用户的userAgent值未包含windows信息，所以默认继承window下的接口
	util.extend(AC, winXCert);
}
AC.main = function(userDefineOpt) {
	//确保初始化一次
	if (initialized) return;
	initialized = true;
	util.extend(this.options, userDefineOpt);
	exp.xBoxInit();
	if (AC.isValidBrowser) {
		AC.render();
	}else {
		exp.msg = 'isValidBrowser';
		opt.installCallFun && opt.installCallFun('isValidBrowser');
		opt.isValidBrowserFun && opt.isValidBrowserFun();
	}
};

})();
